package com.js.data.system.controller;

import com.js.data.common.api.CommonResult;
import com.js.data.common.config.RsaProperties;
import com.js.data.common.constant.CommonConstant;
import com.js.data.common.dto.SecurityUserDTO;
import com.js.data.common.exception.ApiException;
import com.js.data.common.utils.RedisUtils;
import com.js.data.common.utils.RsaUtils;
import com.js.data.common.utils.SecurityUtils;
import com.js.data.common.utils.StringUtils;
import com.js.data.system.annotation.Log;
import com.js.data.system.dto.AuthUserDTO;
import com.js.data.system.dto.JwtUserDTO;
import com.js.data.system.entity.SysConfig;
import com.js.data.system.security.bean.LoginProperties;
import com.js.data.system.security.bean.SecurityProperties;
import com.js.data.system.security.service.OnlineUserService;
import com.js.data.system.security.token.TokenProvider;
import com.js.data.system.service.ConfigService;
import com.js.data.system.vo.LoginVO;
import com.wf.captcha.base.Captcha;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;

/**
 * 登录控制层
 */
@RestController
public class LoginController {

    @Resource
    private LoginProperties loginProperties;

    @Resource
    private OnlineUserService onlineUserService;

    @Resource
    private AuthenticationManagerBuilder authenticationManagerBuilder;

    @Resource
    private TokenProvider tokenProvider;

    @Resource
    private SecurityProperties properties;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private ConfigService configService;
    private final static String LOGIN_LOCK_KEY = "login:lock:";
    private static final String LOGIN_KEY = "login:";
    @Value("${login.login-code.expiration:120}")
    private static final long CAPTCHA_CODE_TIME_OUT = 120L;

    @Log(value = "用户登录")
    @PostMapping(value = "/auth/login")
    public CommonResult<LoginVO> login(@Validated @RequestBody AuthUserDTO authUser) throws Exception {
        //校验验证码
        SysConfig sysConfig = configService.getById(1);
        // 开启验证码
        if ("0".equals(sysConfig.getCheckCodeFlag())) {
            // 查询验证码
            String code = (String) redisUtils.getValue(LOGIN_KEY + authUser.getUuid());
            // 清除验证码
            redisUtils.deleteValue(LOGIN_KEY + authUser.getUuid());
            if (StringUtils.isBlank(code)) {
                throw new ApiException("验证码不存在或已过期");
            }
            if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
                throw new ApiException("验证码错误");
            }
        }
        return  noCodeLogin(authUser);
    }
    @Log(value = "无验证码登录")
    @PostMapping(value = "/auth/noCodeLogin")
    public CommonResult<LoginVO> noCodeLogin(@Validated @RequestBody AuthUserDTO authUser) throws Exception {
        String lockKey = LOGIN_LOCK_KEY + authUser.getUserName();
        if (redisUtils.getValue(lockKey) != null && (Integer) redisUtils.getValue(lockKey) > 5) {
            throw new ApiException("密码错误次数过多,账号被锁定");
        }
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 相同账号密码错误5次被锁定
        String newUserName = authUser.getUserName() + CommonConstant.LOGIN_EXT_STR;
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(newUserName, password);
        Authentication authentication;
        try {
            authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        } catch (AuthenticationException e) {
            redisUtils.incr(lockKey, 1);
            redisUtils.expire(lockKey, 300);
            throw new ApiException(e.getMessage());
        }
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        final JwtUserDTO jwtUserDto = (JwtUserDTO) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token);
        if (loginProperties.isSingleLogin()) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUserName(), token);
        }
        // 返回 token 与 用户信息
        LoginVO loginVO = new LoginVO();
        loginVO.setToken(properties.getTokenStartWith() + token);
        SecurityUserDTO user = jwtUserDto.getUser();
        loginVO.setDeptId(user.getDeptId());
        loginVO.setDeptName(user.getDeptName());
        loginVO.setUserName(jwtUserDto.getUsername());
        loginVO.setNickName(jwtUserDto.getNickName());
        loginVO.setUserMenu(user.getUserMenu());
        return CommonResult.success(loginVO);
    }
    @Log(value = "用户退出")
    @DeleteMapping(value = "/auth/logout")
    public CommonResult<Object> logout(HttpServletRequest request) {
        String currentUsername = SecurityUtils.getCurrentUsername();
        onlineUserService.logout(tokenProvider.getToken(request), currentUsername);
        return CommonResult.success();
    }
    @GetMapping(value = "/auth/captcha")
    public void captchaCode(@NotNull(message = "uuid不能为空") String uuid, HttpServletResponse response) {
        try {
            Captcha captcha = loginProperties.getCaptcha();
            response.setContentType(MediaType.IMAGE_PNG_VALUE);
            response.setHeader(HttpHeaders.PRAGMA, "No-cache");
            response.setHeader(HttpHeaders.CACHE_CONTROL, "No-cache");
            response.setDateHeader(HttpHeaders.EXPIRES, 0L);
            redisUtils.setValue(LOGIN_KEY + uuid, captcha.text(), CAPTCHA_CODE_TIME_OUT);
            captcha.out(response.getOutputStream());
        } catch (IOException ex) {
            throw new ApiException(ex.getMessage());
        }
    }
}
